#include <xpp/net/TcpSocket.h>
#include <xpp/net/Tcp4Address.h>
#include <xpp/net/Tcp6Address.h>
#include <xp/bas/string.h>
#include <xp/bas/stdio.h>
#include <string.h>

using namespace xpp;
using namespace xpp::bas;
using namespace xpp::net;

void print_address (const xp_char_t* title, const InetAddress& addr)
{
	xp_char_t hbuf0[100], sbuf0[100];
	xp_char_t hbuf1[NI_MAXHOST + 1], sbuf1[NI_MAXSERV + 1];

	if (addr.getNumericHost (hbuf0, xp_countof(hbuf0)) == -1) 
		xp_strxcpy (hbuf0, xp_countof(hbuf0), XP_TEXT("*unknown*"));
	if (addr.getNumericService (sbuf0, xp_countof(sbuf0)) == -1)
		xp_strxcpy (sbuf0, xp_countof(sbuf0), XP_TEXT("*unknown*"));
	if (addr.getHostName (hbuf1, xp_countof(hbuf1)) == -1) 
		xp_strxcpy (hbuf1, xp_countof(hbuf1), XP_TEXT("*unknown*"));
	if (addr.getServiceName (sbuf1, xp_countof(sbuf1)) == -1)
		xp_strxcpy (sbuf1, xp_countof(sbuf1), XP_TEXT("*unknown*"));

	xp_printf (XP_TEXT("%s\n"), title);
	xp_printf (
		XP_TEXT("\tisLoopBack: %d isAnyLocal: %d\n"),
		addr.isLoopBack(), addr.isAnyLocal());
	xp_printf (
		XP_TEXT("\tNumeric: %s,%s Name: %s,%s\n"), hbuf0, sbuf0, hbuf1, sbuf1);
}

extern "C" { int xp_main (); }

int xp_main ()
{

	if (Socket::startup() == -1) {
		xp_printf (XP_TEXT("can not initialize the socket subsystem\n"));
		return -1;
	}

	try {
	//InetAddress target (XP_TEXT("http"), XP_TEXT("localhost"), InetAddress::V6);
	//InetAddress target (XP_TEXT("ssh"), XP_TEXT("localhost"), InetAddress::V6);
	//InetAddress target (XP_TEXT("ssh"), XP_TEXT("bacon"), InetAddress::V6);
	//InetAddress target (XP_TEXT("ssh"), XP_TEXT("localhost"), InetAddress::V4);
	//Tcp4Address target (XP_TEXT("ssh"), XP_TEXT("bacon"));
	Tcp4Address target (XP_TEXT("510"), XP_TEXT("localhost"));

	TcpSocket socket;
	xp_byte_t buf[1000];

	print_address (XP_TEXT("target"), target);

	if (socket.open (target) == -1) {
		xp_printf (XP_TEXT("ERROR: can't open - %d\n"), socket.errorCode());
		Socket::cleanup ();
		return -1;
	}

	if (socket.connect () == -1) {
		xp_printf (XP_TEXT("ERROR: can't connect - %d\n"), socket.errorCode());
		Socket::cleanup ();
		return -1;
	}

	//const char* xxx = "GET / HTTP/1.0\r\n\r\n";
	//socket.send (xxx, strlen(xxx));

	const char* xxx = "NULL 2 27729 52 2 1087878900 1087879031 1087879050 1087879136 1087879258 1087886725 1087886770 1087887193 1087887225 1087887390 1087888413 1087888462 1087888514 1087888941 1087889007 1087889124 1087890347 1087890651 1087890875 1087890928 1087891019 1087891856 1087892529 1087893297 1087893385 1087893683 1087893897 1087894517 1087894530 1087895070 1087895088 1087895258 1087895312 1087895385 1087895416 1087896391 1087896413 1087896594 1087896609 1087898353 1087898726 1087898961 1087899066 1087902310 1087902371 1087902435 1087902497 1087902799 1087903922 1087903977 1087904069 1087904119 1087972503 1087972598 1087972618 1087972912 1087972921 1087972987 1087973004 1087973072 1087973095 1087973159 1087973280 1087973347 1087973369 1087973429 1087973580 1087973642 1087973649 1087973725 1087974302 1087974380 1087974408 1087974463 1087974479 1087974553 1087974567 1087974747 1087974809 1087974841 1087974857 1087975176 1087975182 1087975345 1087976200 1087976275 1087976304 1087976392 1087976545 1087976733 1087976751 1087976814 1087978360 1087978548 1087978912 1087979097 1087979426 1087979615 1088071465 1088071655 1088071690 1088075297 1088075335 1088077160\r\n";

	const char* yyy = "NULL 2 45 2 2 1088507553 1088507578 1088507606 1088507626\r\n";

	socket.enableTimeout (2000);
	for (int i = 0;;i++) {
		xp_ssize_t n;
		if (i % 2) n = socket.send (xxx, strlen(xxx));
		else n = socket.send (yyy, strlen(yyy));
		
		if (n <= 0) {
			xp_printf (XP_TEXT("send error - %d\n"), socket.errorCode());
			break;
		}

		n = socket.receive (buf, xp_sizeof(buf) - 1);
		if (n <= 0) {
			xp_printf (XP_TEXT("receive error - %d\n"), socket.errorCode());
			break;
		}

		buf[n] = '\0';
		printf ("%s", buf);

		if (i > 10) break;
	}
	socket.close ();

	}
	catch (Exception& ex) {
		xp_printf (XP_TEXT("exception: %s - %s\n"),
			XPP_EXCEPTION_NAME(ex), XPP_EXCEPTION_MSG(ex));
	}

	Socket::cleanup ();
	return 0;
}
